from tkinter import *#导入tkinter的内容
from tkinter import messagebox#导入messagebox用于显示
from tkinter import filedialog#用于选择文件与目录
import tkinter as tk#另一种导入tkinter的方式，将tkinter实例定义为tk
import os,sys#系统库
from PIL import Image,ImageTk#pillow库处理图像
class basedesk():#底板
    def __init__(self, master):#构造函数
        self.master = master#传入根函数
        self.master.title("图片格式转换器")#对窗口进行重命名
        self.master.configure(bg='#B1FFF9')#设置窗口背景颜色
        self.master.geometry("800x500")#设置窗口尺寸
        mainwindow(self.master)#传入跟函数到mainwindow类
class mainwindow():#主界面
    def __init__(self, master):#构造函数
        self.master = master
        self.my_old_image = ''#初始化图片数据变量
        self.master.name_label = tk.Label(self.master, text="图片格式转换器", font=("Helvetic", 25, "bold"), relief="raised",cursor="heart")#初始化用于名称显示的label
        self.master.name_label.place(x=180,y=0)#放置用于名称显示的label
        self.master.address_listbox = tk.Listbox(self.master)#初始化用于显示输入图片地址的listbox
        self.master.address_listbox.place(x=10, y=60, width=600, height=30)#放置用于显示输入图片地址的listbox
        self.master.address_btn = tk.Button(self.master, text="选择照片", font=("Helvetic", 15, "bold"),command=self.choose_pic)#初始化用于调用选择输入图片地址的按钮
        self.master.address_btn.place(x=620, y=60, width=100, height=30)#放置用于调用选择输入图片地址的按钮
        self.master.choose_pictype_label=tk.Label(self.master,text='选择是否换底色：',font=("Helvetic", 25, "bold"),bg='#B1FFF9')#初始化用于指示选择是否换底色的label
        self.master.choose_pictype_label.place(x=0,y=100)#放置用于指示选择是否换底色的label
        self.type_omTuple = ("不换底色", "换底色")#用于存放可选选项的数组
        self.type_var = StringVar(self.master)#定义一个用于更改选项的tkinter变量
        self.type_var.set("不换底色")#将定义的用于更改选项的tkinter变量设置为"不换底色"
        self.type_option_paddle = OptionMenu(self.master, self.type_var, *self.type_omTuple)#定义一个选项按钮
        self.type_option_paddle.place(x=280, y=100, width=100, height=40)#放置定义的选项按钮
        self.master.submit_btn = tk.Button(self.master, text="下一步", font=("Helvetic", 30, "bold"), bg='lightgreen',command=self.step2)#定义一个"下一步"按钮
        self.master.submit_btn.place(x=550, y=300)#放置"下一步"按钮
    def choose_pic(self):#选择图片功能函数
        file_path=filedialog.askopenfilename(title=u'选择图片', filetypes=[("PNG", ".png"),("JPG", ".jpg"),("BMP", ".bmp")])#生成选择图片交互窗口
        if file_path=='':#如果没有选择文件就返回
            return
        self.master.address_listbox.delete(0,END)#清空已选图片地址
        self.master.address_listbox.insert(END,file_path)#在图片地址listbox末尾插入刚刚选择的图片地址
        self.my_old_image =file_path#将后端图片地址设置为选择的地址
    def step2(self):#点击下一步后调用的函数
        if self.my_old_image=='':#判断图片地址是否为空
            messagebox.showerror("错误",'没有选择图片')#如果为空，弹出提示框提示没有选择图片
            return
        self.window = tk.Frame(self.master, bg='#FFD966')#初始化一个框架将原本的页面盖住
        self.window.place(x=0, y=0, width=800, height=500)#放置生成的框架
        self.window.show_label = tk.Label(self.window)  # 初始化一个label用于展示图片
        self.window.show_label.place(x=50, y=50, width=144, height=192)#放置生成的label
        self.img =Image.open(self.my_old_image)#读取选择的图片
        self.source_type=self.type_var.get()#读取选择的选底色与否选项
        if self.source_type== "换底色":#如果选择了换底色执行下部分语句
            img = self.img.convert("RGBA")#将图片通道转换
            pixdata = img.load()#加载图片的像素数据
            temper = list(pixdata[10, 10]).copy()#选取位置为[10,10]的像素点作为标准色块
            for y in range(img.size[1]):#竖直方向从上向下遍历
                for x in range(img.size[0]):#水平方向从左向右遍历
                    if (abs(pixdata[x, y][0] - temper[0]) <= 80) and (abs(pixdata[x, y][1] - temper[1]) <= 80) and (
                            (pixdata[x, y][2] - temper[2]) <= 80):#判断前后色块的色差是否小于阈值
                        pixdata[x, y] = (255, 255, 255, 255)#将色块涂白
                    else:
                        break
            for y in range(img.size[1]):#竖直方向从上向下遍历
                for x in range(img.size[0]):#水平方向从右向左遍历
                    if (abs(pixdata[img.size[0] - 1 - x, y][0] - temper[0]) <= 80) and (
                            abs(pixdata[img.size[0] - 1 - x, y][1] - temper[1]) <= 80) and (
                            (pixdata[img.size[0] - 1 - x, y][2] - temper[2]) <= 80):#判断前后色块的色差是否小于阈值

                        pixdata[img.size[0] - 1 - x, y] = (255, 255, 255, 255)#将色块涂白
                    else:
                        break
            self.my_middle_image = self.my_old_image + '_no_bg.png'#设置图片暂时保存的路径
            img.save(self.my_middle_image)#暂时保存图片
            fp = open(self.my_middle_image, 'rb')#用另一种方式读取图片
            self.img = Image.open(fp)#获得读取的图片数据
        size=list(self.img.size)#得到图片的尺寸
        self.mine_width=size[0]#得到图片的宽度
        self.mine_height=size[1]#得到图片的长度
        self.init_data=self.factory(size)#获得图片初始展示位置参数
        self.data_record=list(self.init_data)#转换初始展示位置参数为list格式
        self.initdatause()#展示当前的图片区域
        self.window.submit_btn = tk.Button(self.window, text="上一步", font=("Helvetic", 30, "bold"),fg='white', bg='red',command=self.step2_to_step1)
        self.window.submit_btn.place(x=50, y=400)
        self.window.out_btn = tk.Button(self.window, text="生成图片", font=("Helvetic", 30, "bold"), bg='lightgreen',command=self.output)
        self.window.out_btn.place(x=550, y=400)
        self.window.name_label = tk.Label(self.window, text="调整成像区域", font=("Helvetic", 25, "bold"), relief="raised",cursor="heart").place(x=30,y=0)
        self.window.up_btn = tk.Button(self.window, text="↑", font=("Helvetic", 30, "bold"),command=self.go_up)
        self.window.up_btn.place(x=310, y=50,width=70,height=70)
        self.window.down_btn = tk.Button(self.window, text="↓", font=("Helvetic", 30, "bold"),command=self.go_down)
        self.window.down_btn.place(x=310, y=130,width=70,height=70)
        self.window.left_btn = tk.Button(self.window, text="←", font=("Helvetic", 30, "bold"),command=self.go_left)
        self.window.left_btn.place(x=230, y=130,width=70,height=70)
        self.window.right_btn = tk.Button(self.window, text="→", font=("Helvetic", 30, "bold"),command=self.go_right)
        self.window.right_btn.place(x=390, y=130,width=70,height=70)
        self.window.big_btn = tk.Button(self.window, text="+", font=("Helvetic", 30, "bold"),command=self.bigger)
        self.window.big_btn.place(x=230, y=50,width=70,height=70)
        self.window.small_btn = tk.Button(self.window, text="-", font=("Helvetic", 30, "bold"),command=self.smaller)
        self.window.small_btn.place(x=390, y=50,width=70,height=70)
        self.window3 = tk.Frame(self.window, bg='#63F034')
        self.window3.place(x=500, y=50, width=250, height=310)
        self.window3.xscrollbar = Scrollbar(self.window3, orient=HORIZONTAL)
        self.window3.yscrollbar = Scrollbar(self.window3)
        self.window3.submitted_listbox = tk.Listbox(self.window3,selectmode=MULTIPLE)
        self.window3.xscrollbar.pack(side=BOTTOM, fill=X)
        self.window3.yscrollbar.pack(side=RIGHT, fill=Y)
        self.window3.submitted_listbox.place(x=0, y=0, width=232, height=292)
        self.window3.xscrollbar.config(command=self.window3.submitted_listbox.xview)
        self.window3.yscrollbar.config(command=self.window3.submitted_listbox.yview)
        self.window3.submitted_listbox.config(xscrollcommand=self.window3.xscrollbar.set, yscrollcommand=self.window3.yscrollbar.set)
        self.window.command_label = tk.Label(self.window, text="命令行", font=("Helvetic", 25, "bold"), relief="raised",cursor="heart")
        self.window.command_label.place(x=570,y=0)
        if self.source_type== "换底色":
            fp.close()
            os.remove(self.my_middle_image)
    def initdatause(self):#设置初始展示效果
        cropped = self.img.crop(self.init_data)#获得裁剪的数据内容
        cropped =cropped.resize((144, 192))#转换裁剪的数据内容为展示的尺寸
        self.ready_to_save=cropped#设置要展示的内容为准备保存的内容
        self.show_label_init(cropped)#展示设置的内容
    def get_FileSize(self):#读取文件占用的存储空间大小
        self.fsize = os.path.getsize(os.path.join(os.path.dirname(__file__),'out.jpg'))#读取输出的临时文件占用的存储空间大小
        self.fsize = self.fsize / float(1024 * 1024)#将存储空间单位转换成MB
        self.window3.submitted_listbox.insert(END, '生成图片所占内存空间调整为：'+str(round(self.fsize,2))+'MB')#在模拟命令行中打印调整的结果
    def output(self):#点击输出按钮后调用的函数
        self.window3.submitted_listbox.insert(END, '开始输出')#在模拟命令行中打印开始输出的提示
        self.ready_to_save.save(os.path.join(os.path.dirname(__file__),'out.png'))#将图片临时保存成png后缀名的图片
        try:
            os.rename(os.path.join(os.path.dirname(__file__),'out.png'), os.path.join(os.path.dirname(__file__),'out.jpg'))#尝试将png后缀名的图片重命名为jpg图片
        except:
            os.remove( os.path.join(os.path.dirname(__file__),'out.jpg'))#失败了可能是目标文件已存在，删除目标文件
            os.rename(os.path.join(os.path.dirname(__file__),'out.png'), os.path.join(os.path.dirname(__file__),'out.jpg'))#重新命名
        self.window3.submitted_listbox.insert(END, '尺寸调整完毕')#提示调整完成图片的尺寸
        self.window3.submitted_listbox.insert(END, '检验生成图片是否大于200KB')#提示即将开始检验图片所占存储空间是否大于200KB
        self.get_FileSize()#读取文件存储空间
        self.i=0#防止无法调整的图片陷入死循环
        while self.fsize>0.200:#循环判断图片存储空间是否过大
            if self.i>=100:#陷入过多循环则跳出
                break
            self.change_picsize_lower()#将图片存储空间调整小一些
            self.get_FileSize()#重新读取文件存储空间
            self.i += 1#记录循环次数
        self.window3.submitted_listbox.insert(END, '检验生成图片是否小于20KB')#提示即将开始检验图片所占存储空间是否小于20KB
        while self.fsize<0.020:#循环判断图片存储空间是否过小
            if self.i>=100:#陷入过多循环则跳出
                break
            self.change_picsize_higher()#将图片存储空间调整大一些
            self.get_FileSize()#重新读取文件存储空间
            self.i += 1#记录循环次数
        if self.fsize>0.020 and self.fsize<0.200:#判断最终尺寸是否满足条件
            self.window3.submitted_listbox.insert(END, "输出成功，请使用‘out.jpg’")#提示输出符合条件图片成功
            messagebox.showinfo("通知","输出成功，请使用‘out.jpg’")#弹出提示框说明输出成功
            os.startfile(os.path.dirname(__file__))#打开输出的图片目录位置
        else:
            self.window3.submitted_listbox.insert(END, '图像存储空间没有调整成功')#提示没能成功调整图片占用的存储空间
            messagebox.showerror("错误","图像存储空间没有调整成功")#弹出提示框提示没能成功调整图片占用的存储空间
    def change_picsize_lower(self):#将图片占用存储空间调小一些
        im = Image.open(os.path.join(os.path.dirname(__file__),'out.jpg')).convert('RGB')#打开图片
        im.save(os.path.join(os.path.dirname(__file__),'out.jpg'),quality=95)#将图片的质量调整低一些以减小占用存储空间

    def change_picsize_higher(self):
        im = Image.open(os.path.join(os.path.dirname(__file__),'out.jpg'))#打开图片
        im.save(os.path.join(os.path.dirname(__file__),'out.bmp'))#将图片转成bmp格式以增加占用存储空间
        try:
            os.rename(os.path.join(os.path.dirname(__file__),'out.bmp'), os.path.join(os.path.dirname(__file__),'out.jpg'))#尝试将bmp后缀名的图片重命名为jpg图片
        except:
            os.remove( os.path.join(os.path.dirname(__file__),'out.jpg'))#失败了可能是目标文件已存在，删除目标文件
            os.rename(os.path.join(os.path.dirname(__file__),'out.bmp'), os.path.join(os.path.dirname(__file__),'out.jpg'))#重新命名
    def go_right(self):#调整成像区域向右
        temp=list(self.init_data)#将数据转换成列表格式
        if temp[2]>=self.mine_width:#判断成像区域是否到达最右边
            self.window3.submitted_listbox.insert(END,'已经到最右面啦')
            return
        temp[0]+=1#设置左边界
        temp[2]+=1#设置右边界
        temp=tuple(temp)#调整数据格式回到tuple格式
        temp=(temp)
        self.init_data=temp#修改当前位置值
        self.initdatause()#更新画面
    def go_left(self):#调整成像区域向左
        temp=list(self.init_data)#将数据转换成列表格式
        if temp[0]<=0:#判断成像区域是否到达最左边
            self.window3.submitted_listbox.insert(END,'已经到最左面啦')
            return
        temp[0]-=1#设置左边界
        temp[2]-=1#设置右边界
        temp=tuple(temp)#调整数据格式回到tuple格式
        temp=(temp)
        self.init_data=temp#修改当前位置值
        self.initdatause()#更新画面
    def go_down(self):#调整成像区域向下
        temp = list(self.init_data)#将数据转换成列表格式
        if temp[3] >= self.mine_height:#判断成像区域是否到达最下边
            self.window3.submitted_listbox.insert(END,'已经到最下面啦')
            return
        temp[1] += 1#设置上边界
        temp[3] += 1#设置下边界
        temp = tuple(temp)#调整数据格式回到tuple格式
        temp = (temp)
        self.init_data = temp#修改当前位置值
        self.initdatause()#更新画面
    def go_up(self):#调整成像区域向上
        temp = list(self.init_data)#将数据转换成列表格式
        if temp[1]<=0:#判断成像区域是否到达最上边
            self.window3.submitted_listbox.insert(END,'已经到最上面啦')
            return
        temp[1] -= 1#设置上边界
        temp[3] -= 1#设置下边界
        temp = tuple(temp)#调整数据格式回到tuple格式
        temp = (temp)
        self.init_data = temp#修改当前位置值
        self.initdatause()#更新画面
    def bigger(self):#放大图像
        temp = list(self.init_data)#将数据转换成列表格式
        if (temp[3]-temp[1])<=58 or (temp[2]-temp[0])<=73:#判断放大程度
            self.window3.submitted_listbox.insert(END,'已经不能再放大啦')
            return
        temp[2] -= 58#缩小成像区域
        temp[3] -= 73#缩小成像区域
        temp = tuple(temp)#调整数据格式回到tuple格式
        temp = (temp)
        self.init_data = temp#修改当前位置值
        self.initdatause()#更新画面
    def smaller(self):
        temp = list(self.init_data)#将数据转换成列表格式
        if temp==self.data_record.copy():#判断是否到达最大成像区域
            self.window3.submitted_listbox.insert(END, '已经很小啦')
            return
        if self.mine_width-58<temp[2]:#判断缩小前原本的位置
            bar_x=self.mine_width-temp[2]
        else:
            bar_x =58
        if self.mine_height-73<temp[3]:#判断缩小前原本的位置
            bar_y=self.mine_height-temp[3]
        else:
            bar_y =73
        temp[0]-=(58-bar_x)
        temp[1] -= (73 - bar_y)
        temp[2] += bar_x
        temp[3] += bar_y
        if temp[0]<0 or temp[1]<0 or temp[2]>self.mine_width or temp[3]>self.mine_height:#缩小达到一定水平直接恢复初始尺寸
            temp=self.data_record.copy()
        temp = tuple(temp)
        temp = (temp)
        self.init_data = temp
        self.initdatause()
    def factory(self,size):#根据图片尺寸返回初始展示位置参数
        if size[0]/size[1]>232/292:#比较长的照片
            ini_data_return=((0, 0, int((232/292)*size[1]), size[1]))
        else:#比较高的照片
            ini_data_return = ((0, 0, size[0], int(size[0]/(232/292))))
        return ini_data_return
    def show_label_init(self,data):#更新图片画面
        self.window.show_label.destroy()
        self.data = ImageTk.PhotoImage(data)
        self.window.show_label=tk.Label(self.window,image=self.data)#把图片以标签的格式插入
        self.window.show_label.place(x=50, y=50,width=144,height=192)
    def step2_to_step1(self):#回到第一步
        self.master.address_listbox.delete(0,END)
        self.my_old_image = ''
        self.window.destroy()

if __name__ == '__main__':#主函数
    root = tk.Tk()
    root.resizable(False, False)
    basedesk(root)
    root.mainloop()